package com.mischa.atc.tests;

import junit.framework.Assert;

import org.junit.Test;

import com.mischa.atc.Coordinate;

public class CoordinateTest 
{
	@Test
	public void DefaultConstructorCreatesPointAtOrigin()
	{
		Coordinate origin = new Coordinate();
		
		Assert.assertEquals(0.00f, origin.getX());
		Assert.assertEquals(0.00f, origin.getY());
	}
	
	@Test
	public void CoordinatesAreRetainedAfterInitialisation()
	{
		float x = 3.14f;
		float y = -2.73000f;
		
		Coordinate origin = new Coordinate(x, y);
		
		Assert.assertEquals(x, origin.getX());
		Assert.assertEquals(y, origin.getY());
	}
	
	@Test
	public void DifferentObjectsWithSameCoordinatesCompareAsEqual()
	{
		float x = 3.14f;
		float y = -2.73f;
		Coordinate a = new Coordinate(x, y);
		Coordinate b = new Coordinate(x, y);
		
		Assert.assertTrue(a.equals(b));	
	}
	
	@Test
	public void DifferentObjectsWithDifferentCoordinatesCompareUnequal()
	{
		float x = 3.14f;
		float y = -2.73f;
		Coordinate a = new Coordinate(x, y);
		Coordinate b = new Coordinate(y, x);
		
		Assert.assertFalse(a.equals(b));
	}
	
	@Test
	public void ToStringRepresentsCoordinateInStandardForm()
	{
		float x = 3.14f;
		float y = -2.73f;
		String expected = String.format("(%f, %f)", x, y);
		Assert.assertEquals(expected, new Coordinate(x, y).toString());
	}
	
	/*
	 * Calculates the heading from the origin to a point P.
	 * P is obtained by taking a point at 30 compass degrees, and multiplying
	 * the x and y coordinate by xDirection and yDirection, respectively.
	 * Asserts that the resulting heading is equal to the expected value.
	 */
	private void HeadingToPointIsCalculatedCorrectly(int xDirection, int yDirection, float expectedHeading)
	{
		final float WITHIN_PRECISION = 1E-6f;
		float x = (float) (xDirection * Math.sin(Math.toRadians(30)));
		float y = (float) (yDirection * Math.cos(Math.toRadians(30)));
		
		Assert.assertEquals(expectedHeading, new Coordinate(0, 0).headingTo(new Coordinate(x, y)).get(), WITHIN_PRECISION);
	}
	
	@Test
	public void HeadingToPointInDifferentQuadrantsIsCalculatedCorrectly()
	{
		 HeadingToPointIsCalculatedCorrectly(1, 1, 30f);
		 HeadingToPointIsCalculatedCorrectly(0, 1, (float)360);
		 HeadingToPointIsCalculatedCorrectly(1, -1, (float)(180 - 30));
		 HeadingToPointIsCalculatedCorrectly(1, 0, (float)90);
		 HeadingToPointIsCalculatedCorrectly(-1, -1, (float)(180 + 30));
		 HeadingToPointIsCalculatedCorrectly(0, -1, (float)180);
		 HeadingToPointIsCalculatedCorrectly(-1, 1, (float)(360 - 30));
		 HeadingToPointIsCalculatedCorrectly(-1, 0, (float)270);
	}
	
	@Test
	public void HeadingToSamePointIs360ByConvention()
	{
		HeadingToPointIsCalculatedCorrectly(0, 0, (float)360);
	}
	
	@Test
	public void HeadingFromOriginGivesHeadingInProperDirection()
	{
		Coordinate point = new Coordinate(10f, 10f);
		Assert.assertEquals(45f, point.headingFromOrigin().get());
	}
	
	@Test
	public void DistanceBetweenTwoPointsIsCalculatedCorrectly()
	{
		final float WITHIN_PRECISION = 1E-6f;
		// Pythagorean triangle, shifted by (x, y)
		float x = 12.345f;
		float y = -43.21f;
		
		// Coordinate A = new Coordinate(x, y);
		Coordinate B = new Coordinate(x + 3, y);
		Coordinate C = new Coordinate(x, y + 4);
		
		Assert.assertEquals(5f, B.distanceTo(C), WITHIN_PRECISION);
	}
	
	public void DistanceFromOriginIsCalculatedCorrectly()
	{
		// Pythagorean triangle with sides 5 and 12	
		Assert.assertEquals(13f, new Coordinate(5, 12).distanceFromOrigin());
	}
	
}
